﻿using System;
using System.Collections.Generic;
using System.Text;
using SQLRunnerLib.Services;
using SQLRunnerLib.Exceptions;
using System.Xml.Serialization;
using SQLServices.Log;
using System.ComponentModel;

namespace SQLRunnerLib.Project
{

    public enum AuthenticationType
    {
        WindowsAuthentication,
        SQLServerAuthentication
    }

    [Serializable()]
    [System.Xml.Serialization.XmlRoot(ElementName="Environment")]
    public class SQLEnvironment
        : IEditableObject
    {
        private string _name;
        private string _serverName;
        private string _databaseName;
        private AuthenticationType _authentication;
        private string _username;
        private string _password;
        private bool _savePassword = false;
        private bool _isSelected;
        private List<PlaceHolder> _placeHolders;

        private SQLEnvironment environmentClone;

        public SQLEnvironment()
        {
            _authentication = AuthenticationType.WindowsAuthentication;
            _placeHolders = new List<PlaceHolder>();
            _savePassword = false;
            _password = string.Empty;
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string ServerName
        {
            get { return _serverName; }
            set { _serverName = value; }
        }

        public string DatabaseName
        {
            get { return _databaseName; }
            set { _databaseName = value; }
        }

        [XmlIgnore]
        public AuthenticationType Authentication
        {
            get { return _authentication; }
            set { _authentication = value; }
        }

        public bool Trusted
        {
            get { return (_authentication == AuthenticationType.WindowsAuthentication); }
            set { _authentication = value ? AuthenticationType.WindowsAuthentication : AuthenticationType.SQLServerAuthentication; }
        }

        public string Username
        {
            get { return _username; }
            set { _username = value; }
        }

        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }

        public bool SavePassword
        {
            get { return _savePassword; }
            set { _savePassword = value; }
        }

        [XmlIgnore]
        public bool IsSelected
        {
            get { return _isSelected; }
            set { _isSelected = value; }
        }

        public List<PlaceHolder> PlaceHolders
        {
            get { return _placeHolders; }
        }

        /// <summary>
        /// Checks whether
        /// </summary>
        public bool HasFullCredentials
        {
            get
            {
                bool result = false;
                if (Authentication == AuthenticationType.WindowsAuthentication)
                {
                    result = !string.IsNullOrEmpty(ServerName) && !string.IsNullOrEmpty(DatabaseName);
                }
                else
                {
                    result = !string.IsNullOrEmpty(ServerName) && !string.IsNullOrEmpty(DatabaseName) &&
                    !string.IsNullOrEmpty(Username) && !(Password == null);
                }
                return result;
            }
        }

        /// <summary>
        /// This method allows us to centralise validation logic for an environment.
        /// Environments should be able to validate themselves.
        /// </summary>
        public void Validate()
        {
            if (string.IsNullOrEmpty(this.Name))
                throw new ValidationException("Environments must have a name.");
            if (string.IsNullOrEmpty(this.ServerName))
                throw new ValidationException("A server name is required.");
            if (string.IsNullOrEmpty(this.DatabaseName))
                throw new ValidationException("A database name is required.");
            if (!this.Trusted)
            {
                if (string.IsNullOrEmpty(this.Username))
                    throw new ValidationException("Non trusted authentication requires a UserName.");
                if (this.Password == null)
                    throw new ValidationException("Non trusted authentication requires a Password.");
            }
        }

        public bool TestConnection()
        {
            if (HasFullCredentials)
            {
                ConnectToDatabase();
                Disconnect();

                return true;
            }
            else
            {
                return false;
            }
        }

        public void ConnectToServer()
        {
            ServiceContainer.Instance.Logger.LogMessage("Connecting to the SQL Server", MsgLevel.Info);
            ServiceContainer.Instance.SQLConnection.OpenConnection(this.ServerName, this.Username, this.Password, this.Trusted);
            ServiceContainer.Instance.Logger.LogMessage(string.Format("Connected to SQL Server: {0}", this.ServerName), MsgLevel.Info);
        }

        public void ConnectToDatabase()
        {
            ServiceContainer.Instance.Logger.LogMessage("Connecting to the SQL Server", MsgLevel.Info);
            ServiceContainer.Instance.SQLConnection.OpenConnection(this.ServerName, this.Username, this.Password, this.Trusted);
            ServiceContainer.Instance.Logger.LogMessage(string.Format("Connected to SQL Server: {0}", this.ServerName), MsgLevel.Info);
            ServiceContainer.Instance.SQLConnection.GetDatabase(this.DatabaseName);
            ServiceContainer.Instance.Logger.LogMessage(string.Format("Connected to Database: {0}", this.DatabaseName), MsgLevel.Info);
        }


        public void Disconnect()
        {
            ServiceContainer.Instance.Logger.LogMessage("Disconnecting from the SQL Server.", MsgLevel.Info);
            ServiceContainer.Instance.SQLConnection.Disconnect();
        }


        #region IEditableObject Members

        public void BeginEdit()
        {
            environmentClone = new SQLEnvironment();
            environmentClone.Name = this.Name;
            environmentClone.ServerName = this.ServerName;
            environmentClone.DatabaseName = this.DatabaseName;
            environmentClone.Authentication = this.Authentication;
            environmentClone.Username = this.Username;
            environmentClone.Password = this.Password;
            environmentClone.SavePassword = this.SavePassword;
            environmentClone.IsSelected = this.IsSelected;
            foreach (PlaceHolder placeHolder in this.PlaceHolders)
            {
                PlaceHolder placeHolderClone = new PlaceHolder(placeHolder.Name, placeHolder.Value);
                environmentClone.PlaceHolders.Add(placeHolderClone);
            }
        }

        public void CancelEdit()
        {
            if (environmentClone != null)
            {
                this.Name = environmentClone.Name;
                this.ServerName = environmentClone.ServerName;
                this.DatabaseName = environmentClone.DatabaseName;
                this.Authentication = environmentClone.Authentication;
                this.Username = environmentClone.Username;
                this.Password = environmentClone.Password;
                this.SavePassword = environmentClone.SavePassword;
                this.IsSelected = environmentClone.IsSelected;
                this.PlaceHolders.Clear();
                foreach (PlaceHolder placeHolder in environmentClone.PlaceHolders)
                {
                    PlaceHolder placeHolderClone = new PlaceHolder(placeHolder.Name, placeHolder.Value);
                    this.PlaceHolders.Add(placeHolderClone);
                }
            }
        }

        public void EndEdit()
        {
            environmentClone = null;
        }

        #endregion
    }
}
